package rgbos.forum.webClient;

import java.security.MessageDigest;
import java.text.CharacterIterator;
import java.text.StringCharacterIterator;
import java.util.Arrays;
import java.util.Iterator;
import java.util.Map;
import java.util.Vector;

import javax.mail.MessagingException;
import javax.security.auth.login.LoginException;

import org.apache.commons.lang.StringEscapeUtils;

import jaxcent.HtmlElement;
import jaxcent.HtmlPara;
import jaxcent.JaxcentPage;
import jaxcent.Jaxception;
import rgbos.forum.domain.Auditor;
import rgbos.forum.domain.ControlFacade;
import rgbos.forum.domain.ForumCtrlPost;
import rgbos.forum.domain.ForumCtrlTopic;
import rgbos.forum.domain.ForumCtrlUser;
import rgbos.forum.domain.User;
import rgbos.forum.exceptions.HashException;
import rgbos.forum.exceptions.MailException;
import rgbos.forum.persistance.Persistance;
import rgbos.forum.search.AbstractSearchEngine;

public class WebClientHandler {
	//	TODO choose from user list at admin profile. 	
	private ForumCtrlUser fcUser_;
	private ForumCtrlPost fcPost_;
	private ForumCtrlTopic fcTopic_;
	private ControlFacade control_;
	private Persistance pers_;
	private static WebClientHandler handler_;
	
	private  WebClientHandler(boolean connection) {		
		if (connection) {
			pers_  = new Persistance("SQL");
			pers_.connect();
			fcUser_ = new ForumCtrlUser(pers_);
			fcPost_ = new ForumCtrlPost(pers_);
			fcTopic_ = new ForumCtrlTopic(pers_);
			control_ = new ControlFacade(fcUser_, fcPost_, fcTopic_);
		}
	}
	
	public static WebClientHandler getWebClientHandler(){
		if(handler_==null){
			return handler_=new WebClientHandler(true);
		}else{
			return handler_;
		}
	}
	public ControlFacade getControl() {
		return control_;
	}

	public String getClientType(Map sessionData) {
		String userName = (String)sessionData.get("uName");
		String permissions = (String)sessionData.get("permissions");
		
		if (userName == null | permissions == null) {
			return "Guest";
		}
		
		else {
			return permissions;
		}
	}
	
	public void DisplayHeader(HtmlPara para, Map sessionData, final JaxcentPage page)
	throws Jaxception {
		String userName = (String)sessionData.get("uName");
		control_.setOnline(userName);
		String permissions = (String)sessionData.get("permissions");
		
		if (userName == null | permissions == null) {
			para.setInnerHTML("Hello Guest. <a href=\"login.html\">Login</a> | <a href=\"registration.html\">Register</a>"+" | <a href=\"showTopics.html\">Home</a>"+" | <a href=\"search.html\">Search</a>");
		}
		else {
			para.setInnerHTML("Hello <a href=\"showProfile.html\">" + userName + "</a> you are a " + permissions +
					". <a id=\"logout\" href=\"\" onClick=\"return false;\">Logout</a>" +
					" | <a href=\"showTopics.html\">Home</a>" +
					" | <a href=\"search.html\">Search</a>");
			HtmlElement logOutLink = new HtmlElement(page, "logout") {
				protected void onClick( Map pageData ) {								
					try {
						Map sessionData = page.getAllSessionData(false);
						control_.logOut((String)sessionData.get("uName"));
						sessionData.clear();						
						page.navigate( "/showTopics.html" );
					} catch (Jaxception e) {
						Auditor.Log(e.getMessage(), this.getClass());
					}
				}
			};
		}		
	}
	public void displayFooter(HtmlPara para, Map sessionData, final JaxcentPage page) throws Jaxception{
			Vector<User> vu= this.control_.getOnlineUsers();
			String footer;
			if(vu.size()==0){
				footer ="There are no users online at the moment";
			}else if(vu.size()==1){
				footer =vu.elementAt(0).getUsername()+" is online";
			}else{
				footer = "Online users : ";
				int count=0;
				for (User user : vu) {
					if(count!=0)
						footer+=",";
					footer+=user.getUsername();
					count++;
				}
			}
			para.setInnerHTML(footer);
	}

	public String login(String username, String password) throws Exception {		
		String hashedPass = hashString(password);
		if(this.fcUser_.logIn(username, hashedPass)) {
			String mail= "aaa@bb.com"; //doesn't matter;
			User tUser = new User(username,password,mail, this.fcUser_.checkPrems(username));
			System.out.println("The user perm group is " + tUser.getPermmisionGroup());
			if(tUser.getPermmisionGroup() == User.REGULAR) {
				Auditor.Log("User " + username + " has logged in", this.getClass());
				return "User";					
			} 
			else if(tUser.getPermmisionGroup() == User.MODERATOR) {
				Auditor.Log("Moderator " + username + " has logged in", this.getClass());
				return "Moderator";										
			}
			else {
				Auditor.Log("Admin " + username + " has logged in", this.getClass());
				return "Admin";						
			}					
		}
		else { 
			throw new LoginException("Invalid username or password");
		}
	}
	
	public String hashString(String str) throws HashException {
		byte[] passbytes = str.getBytes();
		String hashString = null;
		try{
			MessageDigest algorithm = MessageDigest.getInstance("MD5");
			algorithm.reset();
			algorithm.update(passbytes);
			byte messageDigest[] = algorithm.digest();

			StringBuffer hexString = new StringBuffer();
			for (int i=0;i<messageDigest.length;i++) {
				String hex = Integer.toHexString(0xFF & messageDigest[i]);
				if(hex.length()==1)
					hexString.append('0');
				hexString.append(hex);
				hashString = hexString.toString();
			}
		}
		catch (Exception e) {
			//Auditor.Log("problem with hash ", "severe",this.getClass());
			throw new HashException();
		}
		return hashString;
	}
	
	public boolean register(String username,String password, String mail){
		User tmpUser = new User(username,password,mail,0);
		boolean res = this.fcUser_.registerNewUser(tmpUser);
		return res;
	}

	public AbstractSearchEngine getSearchEngine() {
		return ControlFacade.getSearchEngine();
	}
	
	public String escape(String text) {
		if (text == null)
			return "";
//		String ans = StringEscapeUtils.escapeHtml(text);
		String ans = escapeHtml(text);
		return ans;
	}
	
	   public String escapeHtml(String aText){
		     final StringBuilder result = new StringBuilder();
		     final StringCharacterIterator iterator = new StringCharacterIterator(aText);
		     char character =  iterator.current();
		     while (character != CharacterIterator.DONE ){
		       if (character == '<') {
//		         result.append("&lt;");
		    	 result.append("<");
		       }
		       else if (character == '>') {
//		         result.append("&gt;");
		         result.append(">");
		       }
		       else if (character == '&') {
		         result.append("&"); 
		      }
		       else if (character == '"') {
		         result.append("&quot;");
		       }
		       else if (character == '\t') {
		         addCharEntity(9, result);
		       }
		       else if (character == '!') {
		         addCharEntity(33, result);
		       }
		       else if (character == '#') {
		         addCharEntity(35, result);
		       }
		       else if (character == '$') {
		         addCharEntity(36, result);
		       }
		       else if (character == '%') {
		         addCharEntity(37, result);
		       }
		       else if (character == '\'') {
		         addCharEntity(39, result);
		       }
		       else if (character == '(') {
		         addCharEntity(40, result);
		       }
		       else if (character == ')') {
		         addCharEntity(41, result);
		       }
		       else if (character == '*') {
		         addCharEntity(42, result);
		       }
		       else if (character == '+') {
//		         addCharEntity(43, result);
		         result.append("+");
		       }
		       else if (character == ',') {
//		         addCharEntity(44, result);
		         result.append(",");
		       }
		       else if (character == '-') {
//		         addCharEntity(45, result);
		         result.append("-");
		       }
		       else if (character == '.') {
		         addCharEntity(46, result);
		       }
		       else if (character == '/') {
		         addCharEntity(47, result);
		       }
		       else if (character == ':') {
		         addCharEntity(58, result);
		       }
		       else if (character == ';') {
		         addCharEntity(59, result);
		       }
		       else if (character == '=') {
//		         addCharEntity(61, result);
		    	 result.append("=");
		       }
		       else if (character == '?') {
		         addCharEntity(63, result);
		       }
		       else if (character == '@') {
		         addCharEntity(64, result);
		       }
		       else if (character == '[') {
		         addCharEntity(91, result);
		       }
		       else if (character == '\\') {
		         addCharEntity(92, result);
		       }
		       else if (character == ']') {
		         addCharEntity(93, result);
		       }
		       else if (character == '^') {
		         addCharEntity(94, result);
		       }
		       else if (character == '_') {
		    	 result.append("_");
//		         addCharEntity(95, result);
		       }
		       else if (character == '`') {
		    	   result.append("`");
//		         addCharEntity(96, result);
		       }
		       else if (character == '{') {
		         addCharEntity(123, result);
		       }
		       else if (character == '|') {
		         addCharEntity(124, result);
		       }
		       else if (character == '}') {
		         addCharEntity(125, result);
		       }
		       else if (character == '~') {
		         addCharEntity(126, result);
		       }
		       else {
		         //the char is not a special one
		         //add it to the result as is
		         result.append(character);
		       }
		       character = iterator.next();
		     }
		     return result.toString();
		  }

	   private void addCharEntity(Integer aIdx, StringBuilder aBuilder){
		    String padding = "";
		    if( aIdx <= 9 ){
		       padding = "00";
		    }
		    else if( aIdx <= 99 ){
		      padding = "0";
		    }
		    else {
		      //no prefix
		    }
		    String number = padding + aIdx.toString();
		    aBuilder.append("&#" + number + ";");
		  }		

	
	public String unescape(String text) {
		if (text == null)
			return "";
		return StringEscapeUtils.unescapeHtml(text);
	}
	
	public void forgotPassword(User user) throws MailException {
		String oldPassword = user.getPassword();
		try {
			String newPassword = generateNewPassword(6);
			getControl().logOut(user.getUsername());
			user.setPassword(hashString(newPassword));
			if (getControl().modifyUser(user)) {
				String email = user.getEmail();
				String message = "Your new password is: " + newPassword + "<br>" +
								 "Login to change it to your choice.<br><br>" +
								 "Thank you!<br>" +
								 "RGBOS Team";
				String subject = "New password for RGBOS forum";
				
				sendMail(email, message, subject);
			}
		} catch (HashException e) {
			throw new MailException(e.getMessage());
		} catch (MessagingException e) {
			rollBack(user, oldPassword);
			throw new MailException(e.getMessage());
		}
	}

	private void sendMail(String email, String message, String subject) throws MessagingException {
		Gmail gmail = new Gmail();
		String[] recipients = {email};
		gmail.sendSSLMessage(recipients, subject, message, "rgbos@rgbos.com");
	}

	private void rollBack(User user, String oldPassword) {
		user.setPassword(oldPassword);
		getControl().modifyUser(user);
	}

	private String generateNewPassword(int numOfChars) {
		char[] chars = new String("abcdefghjkmnpqrstuvwxyzABCDEFGHJKLMNOPQRSTUVWXYZ0123456789!@#$%&").toCharArray();
		StringBuffer ans = new StringBuffer("");
		for (int i = 0; i < numOfChars; i++) {			
            int randomNumber = (int) (Math.random() * chars.length);
            ans.append(chars[randomNumber]);
		}
		return ans.toString();
	}

	public void unload(Map sessionData) {
		String username = (String) sessionData.get("uName");
		if (username != null)
			control_.setOffline(username);
	}
	
	public boolean isLegal(String text) {
		char[] chars = new String("'\"`~=+\\][{}:;/,").toCharArray();		
		for (int i = 0; i < text.length(); i++) {
			for (int j = 0; j < chars.length; j++) {
				if (chars[j] == text.charAt(i))
					return false;
			}
		}
		return true;
	}
}
